﻿using System;
using System.Net;
using System.Net.Sockets;
using Google.Protobuf.Examples.AddressBook;
using Google.Protobuf;
using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Collections.Generic;

namespace testClient
{


	public class DataStream
	{
		public static Dictionary<int, System.Type> idToProtocols = new Dictionary<int, Type>()
		{
			{1, typeof(MovePosC2S)},
			{2, typeof(UseSkillC2S)},
			{3, typeof(Data)},
		};

		public byte[] buffer = new byte[1024];
		public int readPos = 0;
		public int writePos = 0;

		public void Clear()
		{
			readPos = writePos = 0;
		}
		public UInt16 ReadUInt16()
		{
			return BitConverter.ToUInt16(buffer, 0);
		}
		public byte ReadByte()
		{
			return buffer[0];
		}

		public IMessage ReadData(int typeId)
		{
			var type = idToProtocols[typeId];
			var msg = Activator.CreateInstance(type) as IMessage;

			var ci = new Google.Protobuf.CodedInputStream(buffer, 0, writePos);
			msg.MergeFrom(ci);
			return msg;
		}
	}


	public class MessageProcess
	{


		public ReadState state = ReadState.HEAD;
		public DataStream dataStream = new DataStream();
		private int expectLen = 2;

		private int bodyLength = 0;
		private int typeId = 0;

		public IMessage msgData;
		public void Process(byte[] data, int n)
		{
			var readYet = 0;

			while (n > 0)
			{
				if (state == ReadState.HEAD)
				{
					if (n >= expectLen)
					{
						Array.Copy(data, readYet, dataStream.buffer, dataStream.writePos, expectLen);
						readYet += expectLen;
						dataStream.writePos += expectLen;
						n -= expectLen;

						bodyLength = dataStream.ReadUInt16();
						dataStream.Clear();
						expectLen = 2;

						state = ReadState.TYPE;
						Console.WriteLine("BodyLength: " + bodyLength.ToString());
					}
					else {
						Array.Copy(data, readYet, dataStream.buffer, dataStream.writePos, n);
						readYet += n;
						dataStream.writePos += n;
						n -= n;
					}
				}
				else if (state == ReadState.TYPE)
				{
					if (n >= expectLen)
					{
						Array.Copy(data, readYet, dataStream.buffer, dataStream.writePos, expectLen);
						readYet += expectLen;
						dataStream.writePos += expectLen;
						n -= expectLen;

						typeId = dataStream.ReadUInt16();
						dataStream.Clear();

						expectLen = 1;
						state = ReadState.FRAME_ID;
						Console.WriteLine("TypeId: " + typeId);
					}
					else {
						Array.Copy(data, readYet, dataStream.buffer, dataStream.writePos, n);
						readYet += n;
						dataStream.writePos += n;
						n -= n;
					}
				}
				else if (state == ReadState.FRAME_ID)
				{
					if (n >= expectLen)
					{
						Array.Copy(data, readYet, dataStream.buffer, dataStream.writePos, expectLen);
						readYet += expectLen;
						dataStream.writePos += expectLen;
						n -= expectLen;

						var data1 = dataStream.ReadByte();
						Console.WriteLine("FrameId:" + data1.ToString());
						dataStream.Clear();
						expectLen = bodyLength;

						state = ReadState.BODY;
					}
					else {
						Array.Copy(data, readYet, dataStream.buffer, dataStream.writePos, n);
						readYet += n;
						dataStream.writePos += n;
						n -= n;
					}
				}
				else {
					if (n >= expectLen)
					{
						Array.Copy(data, readYet, dataStream.buffer, dataStream.writePos, expectLen);
						readYet += expectLen;
						dataStream.writePos += expectLen;
						n -= expectLen;

						var data1 = dataStream.ReadData(typeId);
						msgData = data1;
						Console.WriteLine(data1.ToString());

						dataStream.Clear();
						expectLen = 2;

						state = ReadState.HEAD;
					}
					else {
						Array.Copy(data, readYet, dataStream.buffer, dataStream.writePos, n);
						readYet += n;
						dataStream.writePos += n;
						n -= n;
					}
				}
			}
		}
	}


	public enum ReadState
	{
		HEAD,
		TYPE,
		FRAME_ID,
		BODY,
	};

	public class TestUDPClient
	{
		public UdpClient udpClient;

		public TestUDPClient()
		{
			udpClient = new UdpClient();

		}

		private int curFrameId = 1;

		private byte udpFrameId = 0;
		public void Send(string[] s)
		{
			Console.WriteLine("BeforeReceive");
			IMessage msg = null;

			if (s[1] == "pos")
			{
				var x = Convert.ToInt32(s[2]);
				var y = Convert.ToInt32(s[3]);
				var mp = new MovePosC2S()
				{
					Id = World.playerId,
					X = x,
					Y = y,
					FrameId = udpFrameId,
				};
				udpFrameId++;
				//0 - 256 
				msg = mp;
			}
			else if (s[1] == "skill")
			{
				var skillid = Convert.ToInt32(s[2]);
				var sp = new UseSkillC2S()
				{
					SkillId = skillid,
				};
				msg = sp;
			}
			else {
				var data = new Data()
				{
					Data_ = s[1],
				};
				msg = data;
			}


			var memStream = new MemoryStream();
			var co = new Google.Protobuf.CodedOutputStream(memStream);
			msg.WriteTo(co);
			co.Flush();
			var data2 = memStream.ToArray();
			var len = (UInt16)data2.Length;

			var byteArray = new byte[2 + 2 + 1 + len];
			var frameId = curFrameId++;
			if (curFrameId >= 256)
			{
				curFrameId = 1;
			}

			var blen = BitConverter.GetBytes(len);

			var typeId = (UInt16)MainClass.protoIds[msg.GetType()];

			var typeArr = BitConverter.GetBytes(typeId);

			byte fid = (byte)frameId;
			var frameArr = BitConverter.GetBytes(fid);

			//length  typeId frameId pbdata
			Array.Copy(blen, byteArray, 2);
			Array.Copy(typeArr, 0, byteArray, 2, 2);
			Array.Copy(frameArr, 0, byteArray, 4, 1);
			Array.Copy(data2, 0, byteArray, 5, len);

			//byteArray 2 长度  + len 数据
			//var stream = client.GetStream();

			var endPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 4040);

			try
			{
				//stream.Write(byteArray, 0, byteArray.Length);
				udpClient.Send(byteArray, byteArray.Length, endPoint);
			}
			catch (Exception exp)
			{
				Console.WriteLine(exp.ToString());
				return;
			}
		}

		public async Task Receive()
		{

			UdpReceiveResult result;
			try
			{
				//n = await stream.ReadAsync(buffer, 0, 1024);
				result = await udpClient.ReceiveAsync();

			}
			catch (Exception exp)
			{
				Console.WriteLine(exp.ToString());
				return;
			}

			var messageProcess = new MessageProcess();
			messageProcess.Process(result.Buffer, result.Buffer.Length);

			Console.WriteLine("Receive: " + messageProcess.msgData.ToString());
			//messageProcess.msgData
			World.world.SyncBody(messageProcess.msgData);

		}

		public void Close()
		{
		}
	}

}

